Skip to content

[ty] Validate augmented assignment stores - #27545

Open
charliermarsh wants to merge 7 commits into
mainfrom
charlie/ty-2175-augmented-assignment-stores
Open

[ty] Validate augmented assignment stores#27545
charliermarsh wants to merge 7 commits into
mainfrom
charlie/ty-2175-augmented-assignment-stores

Conversation

@charliermarsh

@charliermarsh charliermarsh commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

Previously, augmented assignments validated the operator but not the assignment of its result back to an attribute or subscript:

class A:
    def __add__(self, other: object) -> object:
        return other

class B:
    x: A

b = B()
b.x += 1  # error: [invalid-assignment]

We now validate augmented attribute and subscript stores through the same statement-level assignment machinery as ordinary assignments, including property setters, descriptors, read-only targets, Final, ClassVar, TypedDict entries, and union receivers and keys. Failed operators preserve their recovery types without triggering a write.

We also include augmented assignments when inferring public instance-attribute types, which lets inferred attributes evolve without assignment-specific exemptions. Existing receiver-correlation and unannotated-collection inference limitations remain out of scope.

Closes astral-sh/ty#2175.

@astral-sh-bot astral-sh-bot Bot added the ty Multi-file analysis & type inference label Aug 6, 2026
@astral-sh-bot

astral-sh-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 96.96%. The percentage of expected errors that received a diagnostic held steady at 92.96%. The number of fully passing files held steady at 106/133.

@astral-sh-bot

astral-sh-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

Memory usage report

Summary

Project Old New Diff Outcome
sphinx 168.71MB 169.40MB +0.41% (703.36kB)
prefect 451.60MB 451.66MB +0.01% (57.69kB)
trio 93.47MB 93.52MB +0.06% (56.78kB)
flake8 40.31MB 40.32MB +0.02% (7.80kB)

Significant changes

Click to expand detailed breakdown

sphinx

Name Old New Diff Outcome
infer_definition_types 10.78MB 11.00MB +2.00% (220.61kB)
infer_expression_types_impl 11.42MB 11.58MB +1.46% (170.50kB)
member_lookup_with_policy_inner 4.80MB 4.91MB +2.19% (107.97kB)
infer_expression_type_impl 202.02kB 252.34kB +24.91% (50.32kB)
StaticClassLiteral<'db>::implicit_attribute_inner_ 598.95kB 632.28kB +5.56% (33.33kB)
analyze_non_terminal_call 454.88kB 480.05kB +5.53% (25.17kB)
TypePair 2.10MB 2.12MB +1.05% (22.59kB)
all_narrowing_constraints_for_expression 2.24MB 2.26MB +0.99% (22.58kB)
UnionType 641.86kB 655.95kB +2.20% (14.09kB)
is_redundant_with_impl 764.24kB 773.62kB +1.23% (9.38kB)
MemberLookupKey 2.95MB 2.95MB +0.27% (8.12kB)
infer_scope_types_impl 6.07MB 6.08MB +0.09% (5.82kB)
loop_header_reachability 243.32kB 246.61kB +1.35% (3.29kB)
TupleType 331.98kB 333.53kB +0.47% (1.55kB)
Type<'db>::apply_specialization_inner_::interned_arguments 1.77MB 1.77MB +0.08% (1.48kB)
... 25 more

prefect

Name Old New Diff Outcome
infer_definition_types 34.54MB 34.55MB +0.04% (13.86kB)
member_lookup_with_policy_inner 10.75MB 10.76MB +0.12% (12.80kB)
StaticClassLiteral<'db>::implicit_attribute_inner_ 803.80kB 812.17kB +1.04% (8.37kB)
infer_scope_types_impl 18.76MB 18.77MB +0.03% (5.33kB)
MemberLookupKey 6.81MB 6.82MB +0.05% (3.35kB)
Type<'db>::class_member_with_policy_inner_ 8.68MB 8.69MB +0.03% (2.56kB)
Type<'db>::apply_specialization_inner_::interned_arguments 5.13MB 5.13MB +0.03% (1.56kB)
TypePair 7.70MB 7.71MB +0.02% (1.31kB)
StaticClassLiteral<'db>::try_mro_ 5.07MB 5.07MB +0.02% (1.23kB)
Type<'db>::apply_specialization_inner_ 3.42MB 3.42MB +0.03% (968.00B)
UnionType 1.36MB 1.36MB +0.06% (896.00B)
FunctionType 5.46MB 5.46MB +0.01% (824.00B)
Specialization 4.56MB 4.56MB +0.02% (784.00B)
all_narrowing_constraints_for_expression 4.52MB 4.52MB +0.01% (640.00B)
infer_expression_types_impl 23.35MB 23.35MB +0.00% (576.00B)
... 13 more

trio

Name Old New Diff Outcome
infer_definition_types 4.43MB 4.44MB +0.32% (14.34kB)
infer_expression_types_impl 5.22MB 5.23MB +0.20% (10.43kB)
member_lookup_with_policy_inner 1.43MB 1.44MB +0.55% (8.07kB)
StaticClassLiteral<'db>::implicit_attribute_inner_ 100.01kB 106.32kB +6.31% (6.31kB)
all_narrowing_constraints_for_expression 637.77kB 641.05kB +0.51% (3.28kB)
TypePair 964.31kB 967.59kB +0.34% (3.28kB)
UnionType 293.77kB 295.78kB +0.69% (2.02kB)
MemberLookupKey 1.14MB 1.14MB +0.16% (1.83kB)
infer_scope_types_impl 2.94MB 2.94MB +0.06% (1.73kB)
is_redundant_with_impl 325.38kB 326.71kB +0.41% (1.33kB)
analyze_non_terminal_call 416.48kB 417.52kB +0.25% (1.05kB)
loop_header_reachability 78.89kB 79.93kB +1.33% (1.05kB)
Type<'db>::class_member_with_policy_inner_ 1.16MB 1.16MB +0.06% (752.00B)
StringLiteralType 524.16kB 524.79kB +0.12% (640.00B)
infer_expression_type_impl 43.45kB 43.82kB +0.86% (384.00B)
... 5 more

flake8

Name Old New Diff Outcome
infer_definition_types 1.06MB 1.07MB +0.22% (2.41kB)
member_lookup_with_policy_inner 322.27kB 323.62kB +0.42% (1.34kB)
StaticClassLiteral<'db>::implicit_attribute_inner_ 44.37kB 45.23kB +1.94% (880.00B)
TypePair 231.00kB 231.75kB +0.32% (768.00B)
infer_expression_types_impl 763.27kB 763.85kB +0.08% (588.00B)
all_narrowing_constraints_for_expression 106.58kB 107.05kB +0.45% (488.00B)
infer_scope_types_impl 535.46kB 535.80kB +0.06% (344.00B)
UnionType 75.27kB 75.59kB +0.44% (336.00B)
is_redundant_with_impl 82.05kB 82.37kB +0.38% (320.00B)
MemberLookupKey 276.91kB 277.12kB +0.07% (208.00B)
ExpressionWithContext 18.91kB 18.98kB +0.41% (80.00B)
Type<'db>::apply_specialization_inner_::interned_arguments 219.77kB 219.84kB +0.04% (80.00B)
Type<'db>::apply_specialization_inner_ 138.92kB 138.97kB +0.03% (48.00B)

@astral-sh-bot

astral-sh-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

Lint rule Added Removed Changed
unresolved-attribute 3 34 3
unused-type-ignore-comment 0 11 0
invalid-assignment 10 0 0
not-subscriptable 8 0 0
unsupported-operator 0 0 7
invalid-argument-type 1 0 0
Total 22 45 10

Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.

Raw diff (77 changes)
aiohttp-devtools (https://github.com/aio-libs/aiohttp-devtools)
+ aiohttp_devtools/runserver/serve.py:90:56 error[invalid-argument-type] Argument to function `len` is incorrect: Expected `Sized`, found `bytes | bytearray | Payload | None`

apprise (https://github.com/caronc/apprise)
+ apprise/plugins/telegram.py:1963:13 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method

dd-trace-py (https://github.com/DataDog/dd-trace-py)
- ddtrace/internal/telemetry/writer.py:323:12 error[unsupported-operator] Operator `>` is not supported between objects of type `float` and `EnvVariable[float]`
+ ddtrace/internal/telemetry/writer.py:323:12 error[unsupported-operator] Operator `>` is not supported between objects of type `float | Unknown` and `EnvVariable[float]`

graphql-core (https://github.com/graphql-python/graphql-core)
- tests/pyutils/test_inspect.py:28:46 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- tests/pyutils/test_inspect.py:32:50 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- tests/pyutils/test_inspect.py:37:39 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- tests/pyutils/test_inspect.py:41:44 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- tests/pyutils/test_inspect.py:46:40 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- tests/pyutils/test_inspect.py:50:45 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive

manticore (https://github.com/trailofbits/manticore)
- manticore/platforms/evm.py:2982:9 error[unresolved-attribute] Object of type `Self@add_refund` has no attribute `_refund`
- manticore/platforms/evm.py:2985:9 error[unresolved-attribute] Object of type `Self@sub_refund` has no attribute `_refund`
+ scripts/binaryninja/manticore_viz/__init__.py:149:13 error[invalid-assignment] Invalid subscript assignment with key of type `Literal[0]` and value of type `float` on object of type `list[int]`
+ scripts/binaryninja/manticore_viz/__init__.py:151:17 error[invalid-assignment] Invalid subscript assignment with key of type `Literal[0]` and value of type `float` on object of type `list[int]`

mongo-python-driver (https://github.com/mongodb/mongo-python-driver)
+ pymongo/ssl_support.py:120:13 error[invalid-assignment] Object of type `int | Any` is not assignable to attribute `options` on type `pymongo.pyopenssl_context.SSLContext | ssl.SSLContext`
+ pymongo/ssl_support.py:121:13 error[invalid-assignment] Object of type `int | Any` is not assignable to attribute `options` on type `pymongo.pyopenssl_context.SSLContext | ssl.SSLContext`
+ pymongo/ssl_support.py:122:13 error[invalid-assignment] Object of type `int | Any` is not assignable to attribute `options` on type `pymongo.pyopenssl_context.SSLContext | ssl.SSLContext`
+ pymongo/ssl_support.py:123:13 error[invalid-assignment] Object of type `Any | int` is not assignable to attribute `options` on type `pymongo.pyopenssl_context.SSLContext | ssl.SSLContext`

pandas (https://github.com/pandas-dev/pandas)
+ pandas/tests/arithmetic/test_datetime64.py:2187:13 error[invalid-assignment] Cannot assign to read-only property `values` on object of type `TimedeltaIndex`: Attempted assignment to `TimedeltaIndex.values` here
+ pandas/tests/arithmetic/test_datetime64.py:2190:13 error[invalid-assignment] Cannot assign to read-only property `_values` on object of type `TimedeltaIndex`: Attempted assignment to `TimedeltaIndex._values` here

pip (https://github.com/pypa/pip)
- src/pip/_vendor/pygments/filters/__init__.py:833:12 error[unresolved-attribute] Object of type `Self@__init__` has no attribute `tabs`
- src/pip/_vendor/pygments/filters/__init__.py:834:13 error[unresolved-attribute] Object of type `Self@__init__` has no attribute `tabs`
- src/pip/_vendor/pygments/filters/__init__.py:835:12 error[unresolved-attribute] Object of type `Self@__init__` has no attribute `newlines`
- src/pip/_vendor/pygments/filters/__init__.py:836:13 error[unresolved-attribute] Object of type `Self@__init__` has no attribute `newlines`
- src/pip/_vendor/pygments/filters/__init__.py:842:20 error[unresolved-attribute] Object of type `Self@filter` has no attribute `tabs`
- src/pip/_vendor/pygments/filters/__init__.py:843:24 error[unresolved-attribute] Object of type `Self@filter` has no attribute `newlines`
- src/pip/_vendor/pygments/filters/__init__.py:859:51 error[unresolved-attribute] Object of type `Self@filter` has no attribute `tabs`
- src/pip/_vendor/pygments/filters/__init__.py:859:62 error[unresolved-attribute] Object of type `Self@filter` has no attribute `newlines`

pywin32 (https://github.com/mhammond/pywin32)
- pythonwin/pywin/scintilla/formatter.py:670:24 error[unresolved-attribute] Object of type `Self@RegisterStyle` has no attribute `nextstylenum`
- pythonwin/pywin/scintilla/formatter.py:671:13 error[unresolved-attribute] Object of type `Self@RegisterStyle` has no attribute `nextstylenum`

scikit-learn (https://github.com/scikit-learn/scikit-learn)
+ sklearn/decomposition/_lda.py:155:13 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
+ sklearn/ensemble/_hist_gradient_boosting/gradient_boosting.py:873:25 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
+ sklearn/preprocessing/_polynomial.py:1079:25 error[invalid-assignment] Invalid subscript assignment with key of type `tuple[slice[None, None, None], slice[None, Unknown | int, None]]` and value of type `lil_array[float64]` on object of type `lil_array[float64]`

scipy (https://github.com/scipy/scipy)
- scipy/optimize/_hessian_update_strategy.py:214:29 error[unresolved-attribute] Attribute `dtype` is not defined on `None`, `int`, `float*` in union `None | ndarray[tuple[int, int], dtype[Any]] | float | ndarray[tuple[Any, ...], dtype[Any]]`
+ scipy/optimize/_hessian_update_strategy.py:214:29 error[unresolved-attribute] Attribute `dtype` is not defined on `None`, `int`, `float*` in union `None | ndarray[tuple[int, int], dtype[Any]] | float | ... omitted 3 union elements`
- scipy/optimize/_hessian_update_strategy.py:217:29 error[unresolved-attribute] Attribute `dtype` is not defined on `None`, `int`, `float*` in union `None | ndarray[tuple[int, int], dtype[Any]] | float | ndarray[tuple[Any, ...], dtype[Any]]`
+ scipy/optimize/_hessian_update_strategy.py:217:29 error[unresolved-attribute] Attribute `dtype` is not defined on `None`, `int`, `float*` in union `None | ndarray[tuple[int, int], dtype[Any]] | float | ... omitted 3 union elements`
- scipy/optimize/_hessian_update_strategy.py:239:21 error[unsupported-operator] Operator `*=` is not supported between objects of type `None` and `float* | ndarray[tuple[Any, ...], dtype[Any]]`
+ scipy/optimize/_hessian_update_strategy.py:239:21 error[unsupported-operator] Operator `*=` is not supported between objects of type `None` and `float* | ndarray[tuple[Any, ...], dtype[Unknown]]`
- scipy/optimize/_hessian_update_strategy.py:244:21 error[unsupported-operator] Operator `*=` is not supported between objects of type `None` and `float* | ndarray[tuple[Any, ...], dtype[Any]]`
+ scipy/optimize/_hessian_update_strategy.py:244:21 error[unsupported-operator] Operator `*=` is not supported between objects of type `None` and `float* | ndarray[tuple[Any, ...], dtype[Unknown]]`
+ scipy/optimize/_nonlin.py:1366:9 error[not-subscriptable] Cannot subscript object of type `None` with no `__getitem__` method
+ scipy/optimize/_slsqp_py.py:370:9 error[invalid-assignment] Invalid subscript assignment with key of type `Unknown` and value of type `tuple[dict[str, Unknown], ...]` on object of type `dict[str, tuple[()]]`
+ scipy/signal/_spectral_py.py:1284:9 error[not-subscriptable] Cannot subscript object of type `Literal[0]` with no `__getitem__` method
+ scipy/signal/_spectral_py.py:1412:9 error[not-subscriptable] Cannot subscript object of type `Literal[0]` with no `__getitem__` method
- scipy/special/_orthogonal.py:187:9 error[unresolved-attribute] Object of type `Self@_scale` has no attribute `_coeffs`

spack (https://github.com/spack/spack)
- lib/spack/spack/vendor/macholib/MachO.py:411:13 error[unsupported-operator] Operator `+` is not supported between objects of type `None | Unknown` and `None | int`
+ lib/spack/spack/vendor/macholib/MachO.py:411:13 error[unsupported-operator] Operator `+` is not supported between objects of type `None | Unknown` and `None | int | Unknown`
- lib/spack/spack/vendor/macholib/MachO.py:419:21 error[unsupported-operator] Operator `+` is not supported between objects of type `None | Unknown` and `None | int`
+ lib/spack/spack/vendor/macholib/MachO.py:419:21 error[unsupported-operator] Operator `+` is not supported between objects of type `None | Unknown` and `None | int | Unknown`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:35:9 error[unresolved-attribute] Unresolved attribute `_count` on type `Self@__new__`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:41:29 error[unresolved-attribute] Object of type `Self@__new__` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:45:16 error[unresolved-attribute] Object of type `Self@__len__` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:59:22 error[unresolved-attribute] Object of type `Self@__getitem__` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:81:64 error[unresolved-attribute] Object of type `Self@__eq__` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:274:18 error[unresolved-attribute] Object of type `Self@set` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:276:21 error[unresolved-attribute] Object of type `Self@set` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:280:38 error[unresolved-attribute] Object of type `Self@set` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:282:34 error[unresolved-attribute] Object of type `Self@set` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:284:17 error[unresolved-attribute] Object of type `Self@set` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:317:13 error[unresolved-attribute] Object of type `Self@_create_new_root` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:329:34 error[unresolved-attribute] Object of type `Self@append` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:333:30 error[unresolved-attribute] Object of type `Self@append` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:350:9 error[unresolved-attribute] Object of type `Self@_mutating_fill_tail` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:361:29 error[unresolved-attribute] Object of type `Self@_mutating_extend` has no attribute `_count`
- lib/spack/spack/vendor/pyrsistent/_pvector.py:389:23 error[unresolved-attribute] Object of type `Self@_push_tail` has no attribute `_count`

spark (https://github.com/apache/spark)
+ python/pyspark/pandas/tests/computation/test_describe.py:61:9 error[unresolved-attribute] Unresolved attribute `A` on type `DataFrame`
+ python/pyspark/pandas/tests/computation/test_describe.py:105:9 error[unresolved-attribute] Unresolved attribute `A` on type `DataFrame`
+ python/pyspark/pandas/tests/computation/test_describe.py:131:9 error[unresolved-attribute] Unresolved attribute `A` on type `DataFrame`
+ python/pyspark/pandas/tests/series/test_string_ops_basic.py:59:9 error[not-subscriptable] Cannot subscript object of type `Index` with no `__getitem__` method

static-frame (https://github.com/static-frame/static-frame)
- static_frame/core/type_blocks.py:4165:67 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- static_frame/core/type_blocks.py:4207:67 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- static_frame/core/type_blocks.py:4223:63 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive

stone (https://github.com/dropbox/stone)
- stone/frontend/lexer.py:69:34 error[unsupported-operator] Operator `>` is not supported between objects of type `None | int` and `Literal[0]`
+ stone/frontend/lexer.py:69:34 error[unsupported-operator] Operator `>` is not supported between objects of type `None | int | Unknown` and `Literal[0]`
- stone/frontend/lexer.py:78:46 error[unsupported-operator] Operator `*` is not supported between objects of type `list[Unknown]` and `None | int`
+ stone/frontend/lexer.py:78:46 error[unsupported-operator] Operator `*` is not supported between objects of type `list[Unknown]` and `None | int | Unknown`

sympy (https://github.com/sympy/sympy)
- sympy/geometry/polygon.py:1695:16 error[unresolved-attribute] Object of type `Self@rotation` has no attribute `_rot`
- sympy/geometry/polygon.py:1943:9 error[unresolved-attribute] Object of type `Self@spin` has no attribute `_rot`
- sympy/geometry/polygon.py:1965:9 error[unresolved-attribute] Object of type `Self@rotate` has no attribute `_rot`
- sympy/geometry/polygon.py:2048:15 error[unresolved-attribute] Object of type `Self@vertices` has no attribute `_rot`
- sympy/geometry/polygon.py:1533:53 error[unresolved-attribute] Object of type `Self@args` has no attribute `_rot`
+ sympy/logic/boolalg.py:3020:21 error[not-subscriptable] Cannot subscript object of type `int` with no `__getitem__` method
- sympy/matrices/repmatrix.py:743:24 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- sympy/matrices/repmatrix.py:747:24 warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive
- sympy/physics/continuum_mechanics/column.py:565:23 error[unresolved-attribute] Object of type `int` has no attribute `subs`
+ sympy/physics/continuum_mechanics/column.py:565:23 error[unresolved-attribute] Attribute `subs` is not defined on `int` in union `int | Expr | Unknown`

Full report with detailed diff (timing results)

@charliermarsh
charliermarsh force-pushed the charlie/ty-2175-augmented-assignment-stores branch 3 times, most recently from 02a47f6 to b44e0a4 Compare August 6, 2026 22:23
@codspeed-hq

codspeed-hq Bot commented Aug 6, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 111 untouched benchmarks
⏩ 84 skipped benchmarks1


Comparing charlie/ty-2175-augmented-assignment-stores (21f50f7) with main (c4e86fc)

Open in CodSpeed

Footnotes

  1. 84 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@charliermarsh
charliermarsh force-pushed the charlie/ty-2175-augmented-assignment-stores branch 2 times, most recently from 435bc6d to 14bf332 Compare August 7, 2026 00:22
@charliermarsh
charliermarsh force-pushed the charlie/ty-2175-augmented-assignment-stores branch from 14bf332 to bdd36b3 Compare August 7, 2026 02:15
@charliermarsh

Copy link
Copy Markdown
Member Author

@charliermarsh
charliermarsh marked this pull request as ready for review August 7, 2026 11:48
@charliermarsh
charliermarsh requested a review from a team as a code owner August 7, 2026 11:48
@astral-sh-bot
astral-sh-bot Bot requested a review from dhruvmanila August 7, 2026 11:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validate that the type of a variable/attribute/subscript remains consistent with its declared type following an augmented assignment operation

1 participant